using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Net;

namespace net.notdot.Attercop.Client
{
    public partial class SettingsWindow : Form
    {
        private UserSettings user;
        private NetworkSettings network;
        private CspParameters keyParams;
        private long totalShared = 0;

        public SettingsWindow(UserSettings user, NetworkSettings network)
        {
            this.user = user;
            this.network = network;

            InitializeComponent();
        }

        private void LoadSettings()
        {
            txtNick.Text = user.Nick;
            txtPort.Text = user.Port.ToString();
            keyParams = user.KeyParameters;
            txtKeyLength.Text = user.KeyLength.ToString();
            UpdateKeyStatus();

            txtNetworkAddress.Text = network.NetworkAddress.ToString();
            txtHelloInterval.Text = network.HelloInterval.ToString();
            txtOfflineTimeout.Text = network.PeerTimeout.ToString();
            txtRemoveTimeout.Text = network.PeerRemove.ToString();

            lstSharedDirs.Items.Clear();
            foreach (KeyValuePair<string, PathNode> i in user.SharedDirectories)
            {
                lstSharedDirs.Items.Add(new ListViewItem(new string[] { i.Key, i.Value.TotalSizeHuman }));
                totalShared += i.Value.TotalSize;
            }
            lblTotalShared.Text = string.Format("Total Shared: {0}", PathNode.GetHumanSize(totalShared));
        }

        private void UpdateKeyStatus()
        {
            if (keyParams == null)
            {
                lblKeyStatus.Text = "Not generated.";
            }
            else
            {
                int len;
                if(!int.TryParse(txtKeyLength.Text, out len)) {
                    MessageBox.Show("The provided key length is not valid.", "Invalid parameter", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                } else {
                    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(len, keyParams);
                    byte[] modulus = rsa.ExportParameters(false).Modulus;
                    StringBuilder fingerprint = new StringBuilder();
                    for(int i = 0; i < 10; i++)
                        fingerprint.AppendFormat("{0:X} ", modulus[i]);

                    lblKeyStatus.Text = string.Format("Generated. Key length is {0} bits, fingerprint is {1}", rsa.KeySize, fingerprint.ToString());
                }
            }
        }

        private void SaveSettings()
        {
            Match addrMatch = Regex.Match(txtNetworkAddress.Text, "^([0-2]?[0-9]{0,2}(?:\\.[0-2]?[0-9]{0,2}){3}):([0-9]{0,5})$");
            IPEndPoint addr = null;
            if (!addrMatch.Success)
                throw new InvalidOperationException("The provided network address is not valid.");
            try
            {
                addr = new IPEndPoint(IPAddress.Parse(addrMatch.Groups[1].Value), int.Parse(addrMatch.Groups[2].Value));
            }
            catch (Exception)
            {
                throw new InvalidOperationException("The provided network address is not valid.");
            }

            user.Nick = txtNick.Text;
            if (!int.TryParse(txtPort.Text, out user.Port))
                throw new InvalidOperationException("The provided port number is not valid");
            if (!int.TryParse(txtKeyLength.Text, out user.KeyLength))
                throw new InvalidOperationException("The provided key length is not valid");
            user.KeyParameters = keyParams;

            user.Save();

            network.NetworkAddress = addr;
            TimeSpan val;
            if (!TimeSpan.TryParse(txtHelloInterval.Text, out val))
                throw new InvalidOperationException("The provided hello interval is not valid");
            network.HelloInterval = val;
            if (!TimeSpan.TryParse(txtOfflineTimeout.Text, out val))
                throw new InvalidOperationException("The provided offline timeout is not valid");
            network.PeerTimeout = val;
            if (!TimeSpan.TryParse(txtRemoveTimeout.Text, out val))
                throw new InvalidOperationException("The provided peer remove timeout is not valid");
            network.PeerRemove = val;
        }

        private void numericTextChanged(object sender, EventArgs e)
        {
            TextBox t = (TextBox)sender;
            t.Text = Regex.Replace(t.Text, "[^0-9]", "");
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            try
            {
                SaveSettings();
                this.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error occurred saving settings", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void SettingsWindow_Load(object sender, EventArgs e)
        {
            LoadSettings();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            keyParams = new CspParameters();
            keyParams.KeyContainerName = "Attercop";
            keyParams.KeyNumber = 2;
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(user.KeyLength, keyParams);
            rsa.PersistKeyInCsp = true;
            UpdateKeyStatus();
        }

        private void lstSharedDirs_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lstSharedDirs.SelectedItems.Count == 0)
            {
                btnRemove.Enabled = false;
            }
            else
            {
                btnRemove.Enabled = true;
            }
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            string dir = lstSharedDirs.SelectedItems[0].Text;
            totalShared -= user.SharedDirectories[dir].TotalSize;
            lblTotalShared.Text = string.Format("Total Shared: {0}", PathNode.GetHumanSize(totalShared));
            user.TreeBuilder.Index.RemoveNode(user.SharedDirectories[dir]);
            user.SharedDirectories.Remove(dir);
            lstSharedDirs.Items.Remove(lstSharedDirs.SelectedItems[0]);
        }

        private void btnAddFolder_Click(object sender, EventArgs e)
        {
            DialogResult result = addFolderDialog.ShowDialog();
            if (result != DialogResult.OK)
                return;

            string dir = addFolderDialog.SelectedPath;
            PathNode node = user.TreeBuilder.BuildTree(dir);
            totalShared += node.TotalSize;
            lblTotalShared.Text = string.Format("Total Shared: {0}", PathNode.GetHumanSize(totalShared));
            user.SharedDirectories.Add(dir, node);
            lstSharedDirs.Items.Add(new ListViewItem(new string[] { dir, node.TotalSizeHuman }));
            lstSharedDirs.Refresh();
        }
    }
}